<?php
// +----------------------------------------------------------------------
// | INPHP
// +----------------------------------------------------------------------
// | Copyright (c) 2020 https://inphp.cc All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( https://opensource.org/licenses/MIT )
// +----------------------------------------------------------------------
// | Author: lulanyin <me@lanyin.lu>
// +----------------------------------------------------------------------
namespace Inphp\DB\Model;

use Inphp\DB\DB;
use Inphp\DB\Query;

/**
 * 数据库模型基类
 * Class ModelBase
 * @package Inphp\DB\Model
 */
abstract class ModelBase {

    /**
     * 主键
     * @var string
     */
    protected string $primary_key = "id";

    /**
     * Query对象
     * @var Query|null
     */
    protected Query|null $db = null;

    /**
     * 主要用于保存数据
     * @var array
     */
    protected array $data = [];

    /**
     * 表别名
     * @var string
     */
    protected string $as = '';

    /**
     * 表名
     * @var string
     */
    protected string $table_name;

    /**
     * 关联的表
     * @var array
     */
    protected array $join = [];

    /**
     * 关联条件
     * @var array
     */
    protected array $join_where = [];

    /**
     * 关联方式
     * @var array
     */
    protected array $join_type = [];

    /**
     * 最后的自增ID
     * @var array
     */
    public array $last_insert_id = [];

    /**
     * 错误信息
     * @var string
     */
    public string $error_info;

    /**
     * 是否属于连接池（主要用于 swoole 扩展）
     * @var bool
     */
    protected bool $is_swoole_pool = false;

    /**
     * 初始化
     * ModelBase constructor.
     */
    public function __construct()
    {
        !defined("INPHP_SERVICE_PROVIDER") && define("INPHP_SERVICE_PROVIDER", 'fpm');
        $this->is_swoole_pool = INPHP_SERVICE_PROVIDER == 'swoole';
        $this->db = $this->getNewDB();
    }

    /**
     * 设置数据
     * @param $name
     * @param $value
     */
    public function setAttribute($name, $value)
    {
        $this->data[$name] = $value;
    }

    /**
     * 获取数据
     * @param $name
     * @return mixed
     */
    public function getAttribute($name) : mixed
    {
        return $this->data[$name] ?? null;
    }

    /**
     * 魔术方法
     * @param $name
     * @param $value
     */
    public function __set($name, $value)
    {
        // TODO: Implement __set() method.
        $this->setAttribute($name, $value);
    }

    /**
     * 获取表名
     * @param bool $full
     * @param string $type
     * @return string
     */
    public function getTableName(bool $full = false, string $type = 'read') : string
    {
        return ($full ? DB::getTablePrefix($type) : "").$this->table_name;
    }

    /**
     * 动态获取
     * @param $name
     * @return mixed
     */
    public function __get($name): mixed
    {
        // TODO: Implement __get() method.
        return $this->getAttribute($name);
    }

    /**
     * 检测数据对象的值
     * @access public
     * @param string $name 名称
     * @return bool
     */
    public function __isset(string $name): bool
    {
        try {
            if (!array_key_exists($name, $this->data)) {
                $this->getAttribute($name);
            }
            return true;
        } catch (\InvalidArgumentException) {
            return false;
        }

    }

    /**
     * 销毁数据对象的值
     * @access public
     * @param string $name 名称
     * @return void
     */
    public function __unset(string $name)
    {
        unset($this->data[$name]);
    }

    /**
     * 查询别名
     * @param $name
     * @return ModelBase
     */
    public function as($name) : ModelBase{
        $this->as = $name;
        return $this;
    }

    /**
     * 动态重载方法
     * @param $method
     * @param $arguments
     * @return Query
     */
    public function __call($method, $arguments): Query
    {
        // TODO: Implement __call() method.
        $db = $this->mainQuery(1);
        if(!empty($this->join)){
            foreach ($this->join as $k=>$j){
                $db = $db->join($j, $this->join_where[$k], $this->join_type[$k]);
            }
        }
        if (method_exists($this, $method)) {
            array_unshift($arguments, $db);
            return call_user_func_array([$this, $method], $arguments);
        } else {
            return call_user_func_array([$db, $method], $arguments);
        }
    }

    /**
     * 开始查询
     * @return Query
     */
    public function startQuery() : Query
    {
        $db = $this->mainQuery(true);
        if(!empty($this->join)){
            foreach ($this->join as $k=>$j){
                $db = $db->join($j, $this->join_where[$k], $this->join_type[$k]);
            }
        }
        return $db;
    }

    /**
     * 静态查询方法
     * @return Query
     */
    public static function query() : Query
    {
        return (new static())->startQuery();
    }

    /**
     * 开始一个查询对象
     * @param mixed|false $as
     * @return Query
     */
    public static function startEmptyQuery(mixed $as = false): Query
    {
        return (new static())->mainQuery($as);
    }

    /**
     * 获取表名
     * @param bool $full
     * @param string $type
     * @return string
     */
    public static function tableName(bool $full = false, string $type = 'read'): string
    {
        return (new static())->getTableName($full, $type);
    }

    /**
     * 新数据插入
     * @param array $data
     * @return bool
     */
    public function save(array $data = []) : bool
    {
        if(empty($this->data) && empty($data)){
            $this->error_info = "数据为空，不执行插入";
            return 0;
        }
        $query = $this->db->from($this->table_name);
        $query->set(!empty($this->data) ? array_merge($this->data, $data) : $data);
        if($query->insert()){
            $this->last_insert_id = $query->getLastInsertId();
            return true;
        }else{
            $this->error_info = $query->getError();
            return false;
        }
    }

    /**
     * 获取错误信息
     * @return string
     */
    public function getError() : string
    {
        return $this->error_info;
    }

    /**
     * 获取一个新的连接
     * @return Query
     */
    private function getNewDB() : Query
    {
        if(!is_null($this->db)){
            return $this->db->newQuery();
        }
        return new Query();
    }

    /**
     * 主表的Query对象
     * @param mixed $as
     * @return Query
     */
    public function mainQuery(mixed $as = false) : Query
    {
        $db = $this->getNewDB();
        return $db->newQuery()->from($this->table_name.($as ? (is_string($as) ? " {$as}" : (!empty($this->as) ? " {$this->as}" : "")) : ""));
    }

    /**
     * 返回一个空的Query
     * @return Query
     */
    public function emptyQuery() : Query
    {
        return $this->getNewDB();
    }

    /**
     * 获取一行数据
     * @param mixed $primary_key_value
     * @return bool|array
     */
    public static function getRow(mixed $primary_key_value = null) : bool|array
    {
        $m = new static();
        $db = $m->mainQuery();
        if(!is_null($primary_key_value)){
            $db->where($m->primary_key, $primary_key_value);
        }
        return $db->first();
    }

    /**
     * 通过多个条件，获取一行数据
     * @param string|array $param
     * @param mixed|null $value
     * @return bool|array
     */
    public static function getRowByParams(string|array $param, mixed $value = null): bool|array
    {
        $m = new static();
        $db = $m->mainQuery();
        $params = is_array($param) ? $param : [$param, $value];
        return $db->where($params)->first();
    }

    /**
     * 删除一行数据
     * @param $primary_key_value
     * @return bool
     */
    public static function delRow($primary_key_value) : bool{
        $m = new static();
        return $m->mainQuery()->where($m->primary_key, $primary_key_value)->delete();
    }
}